﻿using System;
using System.Numerics;
#region 开发日志
// 1.进行控制台初始化
//申明控制台初始化方法 并在主函数中调用
// 2.实现多个场景切换
//通过while语句和switch语句实现多场景切换
//申明一个场景类型枚举 通过场景枚举变量选择进入不同的场景
// 3.实现开始场景逻辑
//使用while语句循环打印开始场景
//使用switch语句实现 WS改变选项 J确认选项
//申明一个标识符 表示当前选中的选项
//再申明一个标识符 用于实现按下J键 改变当前场景枚举后 退出开始场景循环
// 4.绘制游戏场景中不变的内容 包括场景边界和提示信息
// 5.申明格子的结构体 成员变量包括 格子类型和格子位置
//格子类型申明一个枚举
//格子位置申明一个结构体
//成员方法 如何绘制一个格子
// 6.申明地图结构体 成员变量为格子结构体数组
//初始化格子结构体格子类型成员时 通过1到100随机数与条件分支语句 实现设置不同格子类型出现概率
#endregion
#region 优化
//1.开始场景通过当前选项nowSelIndex的值来选择打印选项的颜色
//2.开始场景实现按键操作逻辑时 调用ConsoleKey枚举读取按键内容
//  使用Console.ReadKey().Key代替Console.ReadKey().KeyChar
//3.将游戏场景方法中绘制不变信息的逻辑写到另一个方法Draw中
//  使代码可读性更高
#endregion
namespace CSharp基础实践
{
    class Program
    {
        static void Main(string[] args)
        {
            #region 控制台初始化
            //申明控制台的宽度与高度
            //在控制台中
            //一个单位高度等于两个单位宽度
            int width = 50;
            int height = 30;
            //调用控制台初始化方法 并传入参数
            ConsoleInit(width, height);
            #endregion

            #region 多场景切换
            //申明一个当前场景的枚举变量
            //将它初始化为开始场景
            E_SceneType nowScene = E_SceneType.Begin;
            //场景切换逻辑(游戏主循环)
            while (true)
            {
                switch (nowScene)
                {
                    case E_SceneType.Begin:
                        //进入一个场景时，应该将先前场景内容清除
                        Console.Clear();
                        //进入开始场景逻辑
                        SceneBegin(width, ref nowScene);
                        break;
                    case E_SceneType.Game:
                        Console.Clear();
                        SceneGame(width, height, ref nowScene);
                        break;
                    case E_SceneType.End:
                        Console.Clear();
                        SceneEnd(width, ref nowScene);
                        break;
                    default:
                        break;
                }
            }
            #endregion
        }

        #region 控制台初始化方法
        //参数：控制台宽度 控制台高度
        static void ConsoleInit(int width, int height)
        {
            //隐藏光标
            Console.CursorVisible = false;
            //游戏舞台大小
            //窗口大小
            Console.SetWindowSize(width, height);
            Console.SetBufferSize(width, height);
            //缓冲区大小
        }
        #endregion

        #region 开始场景方法
        //参数：
        //传入width用于打印前设置光标位置
        //传入ref nowScene用于实现更换游戏场景枚举
        //nowScene是值类型 需要用ref修饰来改变外部的nowScene
        static void SceneBegin(int width, ref E_SceneType nowScene)
        {
            //申明一个标识符
            int nowSelIndex = 1;            //默认选择开始游戏
            bool isContinue = true;         //默认一直进行开始场景循环 按下J键后置为false

            //先打印不变的内容
            Console.SetCursorPosition(width / 2 - 3, 3);
            Console.Write("飞行棋");

            //进入开始场景循环
            while (true)
            {
                //打印随按键输入改变的内容
                Console.SetCursorPosition(width / 2 - 4, 7);
                Console.ForegroundColor = nowSelIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
                Console.Write("开始游戏");
                Console.SetCursorPosition(width / 2 - 4, 9);
                Console.ForegroundColor = nowSelIndex == 2 ? ConsoleColor.Red : ConsoleColor.White;
                Console.Write("结束游戏");

                
                switch (Console.ReadKey(true).Key)
                {
                    case ConsoleKey.W:
                        --nowSelIndex;
                        if(nowSelIndex == 0)
                        {
                            nowSelIndex = 2;
                        }
                        break;
                    case ConsoleKey.S:
                        ++nowSelIndex;
                        if(nowSelIndex == 3)
                        {
                            nowSelIndex = 1;
                        }
                        break;
                    case ConsoleKey.J:
                        switch (nowSelIndex)
                        {
                            case 1:
                                nowScene = E_SceneType.Game;
                                isContinue = false;         //结束开始场景循环
                                break;
                            case 2:
                                Environment.Exit(0);    //直接退出游戏
                                break;
                            default:
                                break;
                        }
                        break;
                    default:
                        //按下其他键，不进行任意操作
                        break;
                }
                //实现按下J键后退出开始场景循环
                if (!isContinue)
                {
                    break;
                }
            }
        }
        #endregion

        #region 游戏场景方法
        static void SceneGame(int width, int height, ref E_SceneType nowScene)
        {
            //绘制不变的场景内容
            DrawWall(width, height);
            //绘制地图
            //申明基本信息
            int num = 99;   //地图长度
            //地图起点坐标
            int x = 6;
            int y = 4;
            //申明地图结构体变量并初始化
            Map map = new Map(num, x, y);
            //调用地图绘制方法
            map.DrawMap(num);
            //绘制玩家
            Player you = new Player(E_Player.you, 0);
            Player ai = new Player(E_Player.ai, 0);
            DrawPlayer(map, you, ai);

            bool isOver = false;
            while(true)
            {
                //实现按键扔骰子
                Console.ReadKey(true);
                //玩家扔骰子
                isOver = RandomMove(width, height, map, ref you, ref ai);
                //绘制地图
                map.DrawMap(num);
                //绘制玩家
                DrawPlayer(map, you, ai);
                //判断是否要结束游戏
                if (isOver)
                {
                    Console.ReadKey(true);
                    nowScene = E_SceneType.End;
                    break;
                }

                //实现按键扔骰子
                Console.ReadKey(true);
                //电脑扔骰子
                isOver = RandomMove(width, height, map, ref ai, ref you);
                //绘制地图
                map.DrawMap(num);
                //绘制玩家
                DrawPlayer(map, you, ai);
                //判断是否要结束游戏
                if (isOver)
                {
                    Console.ReadKey(true);
                    nowScene = E_SceneType.End;
                    break;
                }
                

            }
        }

        #region 绘制不变信息方法
        static void DrawWall(int width, int height)
        {
            //绘制游戏边界
            //将游戏边界设置为红色
            Console.ForegroundColor = ConsoleColor.Red;
            //绘制x轴方向边界
            for (int i = 0; i < width - 1; i++)          //这里width - 1是因为中文字符占两个单位宽度 i最多取到width - 2
            {
                Console.SetCursorPosition(i, 0);
                Console.Write('■');
                Console.SetCursorPosition(i, height - 12);
                Console.Write('■');
                Console.SetCursorPosition(i, height - 7);
                Console.Write('■');
                Console.SetCursorPosition(i, height - 1);
                Console.Write('■');
            }
            //绘制y轴方向边界
            for (int i = 0; i < height; i++)
            {
                Console.SetCursorPosition(0, i);
                Console.Write('■');
                Console.SetCursorPosition(width - 2, i);
                Console.Write('■');
            }
            //打印提示信息
            Console.SetCursorPosition(2, height - 11);
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write("□:普通格子");

            Console.SetCursorPosition(2, height - 10);
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.Write("‖:暂停一回合  ");
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("●炸弹:倒退5格");

            Console.SetCursorPosition(2, height - 9);
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write("¤时空隧道:随机倒退,暂停,交换位置");

            Console.SetCursorPosition(2, height - 8);
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.Write("★:玩家 ");
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.Write("▲:电脑 ");
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write("⊙:电脑与玩家重合");
        }
        #endregion

        #region 绘制玩家
        static void DrawPlayer(Map map, Player you, Player ai)
        {
            
            if(you.nowIndex == ai.nowIndex)
            {
                Grid grid = map.grids[you.nowIndex];
                Console.SetCursorPosition(grid.pos.x, grid.pos.y);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.Write('⊙');
            }
            else
            {
                you.DrawPlayer(map);
                ai.DrawPlayer(map);
            }
        }
        #endregion

        #region 实现扔色子逻辑
        /// <summary>
        /// 扔色子逻辑
        /// </summary>
        /// <param name="width">窗口宽度</param>
        /// <param name="height">窗口高度</param>
        /// <param name="map">地图信息</param>
        /// <param name="player">移动对象</param>
        /// <returns></returns>
        static bool RandomMove(int width, int height, Map map, ref Player player, ref Player other)
        {
            //判断玩家对象是否处于暂停状态
            if(player.isPause)
            {
                player.isPause = false;
                return false;
            }

            Random r = new Random();
            int randomNum = r.Next(1, 7);
            player.nowIndex += randomNum;
            //判断是否抵达终点
            if(player.nowIndex >= map.grids.Length - 1)
            {
                player.nowIndex = map.grids.Length - 1;
                return true;
            }
            else
            {
                Grid grid = map.grids[player.nowIndex];
                switch (grid.type)
                {
                    case E_Grid.normal:
                        //普通格子不处理逻辑
                        break;
                    case E_Grid.boom:
                        //炸弹格子倒退5格
                        player.nowIndex -= 5;
                        if(player.nowIndex <= 0)
                        {
                            player.nowIndex = 0;
                        }
                        break;
                    case E_Grid.pause:
                        //暂停格子暂停一回合
                        //在Player结构体中申明标识isPause 表示是否暂停
                        player.isPause = true;
                        break;
                    case E_Grid.channel:
                        //时空隧道随机倒退,暂停,交换位置
                        Random r2 = new Random();
                        randomNum = r2.Next(1, 91);
                        //三分之一概率倒退
                        if(randomNum <= 30)
                        {
                            player.nowIndex -= 5;
                        }
                        //三分之一暂停一回合
                        else if(randomNum <= 60)
                        {
                            player.isPause = true;
                        }
                        //三分之一交换位置
                        else
                        {
                            int temp = player.nowIndex;
                            player.nowIndex = other.nowIndex;
                            other.nowIndex = temp;
                        }
                        break;
                }
            }

            return false;
        }
        #endregion

        #endregion

        #region 结束场景方法
        //逻辑与开始场景完全一致
        static void SceneEnd(int width, ref E_SceneType nowScene)
        {
            //申明一个标识符
            int nowSelIndex = 1;          //默认选择重新开始
            bool isContinue = true;     //默认一直进行开始场景循环 按下J键后置为false

            //先打印不变的内容
            Console.SetCursorPosition(width / 2 - 4, 3);
            Console.Write("游戏结束");
            
            //进入结束场景循环
            while (true)
            {
                //打印随按键变化的内容
                Console.SetCursorPosition(width / 2 - 4, 7);
                Console.ForegroundColor = nowSelIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
                Console.Write("重新开始");
                Console.SetCursorPosition(width / 2 - 4, 9);
                Console.ForegroundColor = nowSelIndex == 2 ? ConsoleColor.Red : ConsoleColor.White;
                Console.Write("结束游戏");

                switch (Console.ReadKey(true).Key)
                {
                    case ConsoleKey.W:
                        --nowSelIndex;
                        if(nowSelIndex == 0)
                        {
                            nowSelIndex = 2;
                        }
                        break;
                    case ConsoleKey.S:
                        ++nowSelIndex;
                        if(nowSelIndex == 3)
                        {
                            nowSelIndex = 1;
                        }
                        break;
                    case ConsoleKey.J:
                        switch (nowSelIndex)
                        {
                            case 1:
                                nowScene = E_SceneType.Begin;
                                isContinue = false;         //结束 结束场景循环
                                break;
                            case 2:
                                Environment.Exit(0);    //直接退出游戏
                                break;
                            default:
                                break;
                        }
                        break;
                    default:
                        //按下其他键，不进行任意操作
                        break;
                }
                //实现按下J键后退出结束场景循环
                if (!isContinue)
                {
                    break;
                }
            }

        }
        #endregion
    }
    #region 场景类型枚举
    /// <summary>
    /// 场景类型
    /// </summary>
    enum E_SceneType
    {
        /// <summary>
        /// 开始场景
        /// </summary>
        Begin,
        /// <summary>
        /// 游戏场景
        /// </summary>
        Game,
        /// <summary>
        /// 结束场景
        /// </summary>
        End,
    }
    #endregion

    #region 格子类型枚举
    /// <summary>
    /// 格子类型
    /// </summary>
    enum E_Grid
    {
        /// <summary>
        /// 普通格子
        /// </summary>
        normal,
        /// <summary>
        /// 炸弹格子
        /// </summary>
        boom,
        /// <summary>
        /// 暂停格子
        /// </summary>
        pause,
        /// <summary>
        /// 时空隧道
        /// </summary>
        channel,
    }
    #endregion

    #region 位置信息结构体
    struct Vector2
    {
        public int x;
        public int y;

        public Vector2(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    #endregion

    #region 格子结构体
    struct Grid
    {
        //成员变量
        public E_Grid type;
        public Vector2 pos;

        //构造函数
        public Grid(E_Grid type, Vector2 pos)
        {
            this.type = type;
            this.pos = pos;
        }

        //成员方法
        //绘制格子
        //传入参数 格子类型枚举 格子位置信息结构体
        public void DrawGrid()
        {
            if(type == E_Grid.normal)
            {
                Console.SetCursorPosition(pos.x, pos.y);
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write('□');
            }
            else if(type == E_Grid.boom)
            {
                Console.SetCursorPosition(pos.x, pos.y);
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write('●');
            }
            else if(type == E_Grid.pause)
            {
                Console.SetCursorPosition(pos.x, pos.y);
                Console.ForegroundColor = ConsoleColor.Magenta;
                Console.Write('‖');
            }
            else
            {
                Console.SetCursorPosition(pos.x, pos.y);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write('¤');
            }
        }
    }
    #endregion

    #region 地图结构体
    struct Map
    {
        // 1.成员变量 格子结构体数组
        public Grid[] grids;

        // 2.构造函数 先初始化格子结构体数组
        //再分别初始化它的两个成员变量:格子类型(枚举)和格子位置信息(结构体)
        //传入的num是地图上的格子总数
        public Map(int num, int x, int y)
        {
            //初始化格子结构体数组
            grids = new Grid[num];
            //随机数1到100 实现设置不同格子的出现概率
            Random r = new Random();        
            int randomNum = 0;
            //申明计数器记录x轴与y轴坐标的变化次数
            int indexX = 0;
            int indexY = 0;
            //申明x轴每次变化的长度 方便变向、
            int stepNum = 2;
            //遍历数组进行初始化
            for (int i = 0; i < num; i++)
            {
                //初始化格子结构体的 格子类型成员
                randomNum = r.Next(0, 100);
                //普通格子出现概率85%
                if(randomNum < 85)
                {
                    grids[i].type = E_Grid.normal;
                }
                //炸弹格子出现概率5%
                else if (randomNum >= 85 && randomNum < 90)
                {
                    grids[i].type = E_Grid.boom;
                }
                //暂停格子出现概率5%
                else if (randomNum >= 90 && randomNum < 95)
                {
                    grids[i].type = E_Grid.pause;
                }
                //时空隧道出现概率5%
                else
                {
                    grids[i].type = E_Grid.channel;
                }

                //初始化格子结构体的 格子位置信息成员
                grids[i].pos = new Vector2(x, y);

                if (indexX == 18)
                {
                    ++y;
                    ++indexY;
                    if(indexY == 2)
                    {
                        indexX = 0;
                        indexY = 0;
                        stepNum = -stepNum;
                    }
                }
                else
                {
                    x += stepNum;
                    ++indexX;
                }
            }
        }
        public void DrawMap(int num)
        {
            for(int i = 0; i < num; i++)
            {
                grids[i].DrawGrid();
            }
        }
    }
    #endregion

    #region 玩家类型枚举
    /// <summary>
    /// 玩家类型
    /// </summary>
    enum E_Player
    {
        /// <summary>
        /// 你
        /// </summary>
        you,
        /// <summary>
        /// 电脑
        /// </summary>
        ai,
    }
    #endregion

    #region 玩家结构体
    struct Player
    {
        //成员变量:玩家类型(枚举) 位置信息(地图格子下标)
        public E_Player player;
        public int nowIndex;
        public bool isPause;

        //初始化
        public Player(E_Player player, int index, bool isPause = false)
        {
            this.player = player;
            nowIndex = index;
            this.isPause = isPause;
        }

        //成员方法
        public void DrawPlayer(Map map)
        {
            Grid grid = map.grids[nowIndex];
            Console.SetCursorPosition(grid.pos.x, grid.pos.y);
            switch (player)
            {
                case E_Player.you:
                    Console.ForegroundColor = ConsoleColor.Blue;
                    Console.Write('★');
                    break;
                case E_Player.ai:
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.Write('▲');
                    break;
            }
        }
    }
    #endregion
}